﻿<Cmdlet(VerbsLifecycle.Invoke, "SqlBulkCopy")>
Public Class InvokeSqlBulkCopy
    Inherits PSCmdlet

    <Parameter>
    Public Property SourceConnectionName As String = "Default"
    <Parameter>
    Public Property DestinationConnectionName As String = "Default"

    <Parameter(Mandatory:=True, ParameterSetName:="SourceTable")>
    Public Property SourceTable As String
    <Parameter(Mandatory:=True, ParameterSetName:="SourceQuery")>
    Public Property SourceQuery As String
    <Parameter(ParameterSetName:="SourceQuery")>
    Public Property SourceParameters As Hashtable

    <Parameter(ParameterSetName:="SourceTable")>
    <Parameter(Mandatory:=True, ParameterSetName:="SourceQuery")>
    Public Property DestinationTable As String

    <Parameter(ParameterSetName:="SourceTable")>
    <Parameter(ParameterSetName:="SourceQuery")>
    Public Property ColumnMap As Hashtable

    <Parameter>
    Public Property BatchSize As Integer = 100

    <Parameter>
    Public Property BatchTimeout As Integer = 0

    <Parameter>
    Public Property Notify As SwitchParameter

    Protected Overrides Sub EndProcessing()
        MyBase.EndProcessing()
        If Me.SourceConnectionName = Me.DestinationConnectionName Then
            ThrowTerminatingError(New ErrorRecord(New PSArgumentException("You cannot use the same connection for both the source and destination!", "SourceConnectionName, DestinationConnectionName"), Nothing, ErrorCategory.InvalidArgument, Nothing))
        ElseIf Not DBInfo.Connections.ContainsKey(Me.SourceConnectionName) Then
            ThrowTerminatingError(New ErrorRecord(New PSArgumentException(DBInfo.NoConnection(Me.SourceConnectionName), "SourceConnectionName"), Nothing, ErrorCategory.InvalidArgument, Nothing))
        ElseIf Not DBInfo.Connections.ContainsKey(Me.DestinationConnectionName) Then
            ThrowTerminatingError(New ErrorRecord(New PSArgumentException(DBInfo.NoConnection(Me.SourceConnectionName), "DestinationConnectionName"), Nothing, ErrorCategory.InvalidArgument, Nothing))
        End If

        If Me.BatchTimeout = 0 Then Me.BatchTimeout = DBInfo.Connections(Me.DestinationConnectionName).CommandTimeout
        Dim SrcCmd As IDbCommand
        DBInfo.ProcessHashTable(Me.SourceParameters)

        Select Case Me.ParameterSetName
            Case "SourceTable"
                If Me.DestinationTable = String.Empty Then Me.DestinationTable = Me.SourceTable
                Dim query As String = String.Format("SELECT * FROM {0}", Me.SourceTable)
                If Me.ColumnMap IsNot Nothing AndAlso Me.ColumnMap.Count > 0 Then
                    query = String.Format("SELECT {0} FROM {1}", String.Join(", ", Me.ColumnMap.Keys.Cast(Of String)), Me.SourceTable)
                End If
                SrcCmd = DBInfo.Connections(Me.SourceConnectionName).GetCommand(query)
            Case "SourceQuery"
                SrcCmd = DBInfo.Connections(Me.SourceConnectionName).GetCommand(Me.SourceQuery, Me.SourceParameters)
        End Select

        Try
            Using dr As IDataReader = SrcCmd.ExecuteReader()
                If Me.Notify.IsPresent Then
                    WriteObject(DBInfo.Connections(Me.DestinationConnectionName).BulkLoad(dr, Me.DestinationTable, Me.ColumnMap, Me.BatchSize, Me.BatchTimeout, AddressOf BatchNotify))
                    WriteProgress(New ProgressRecord(0, "SimplySql Bulk Copy", "Finished") With {.RecordType = ProgressRecordType.Completed})
                Else
                    WriteObject(DBInfo.Connections(Me.DestinationConnectionName).BulkLoad(dr, Me.DestinationTable, Me.ColumnMap, Me.BatchSize, Me.BatchTimeout, Nothing))
                End If
            End Using
        Catch ex As Exception
            If SrcCmd IsNot Nothing Then SrcCmd.Dispose()
            ThrowTerminatingError(New ErrorRecord(ex, Nothing, ErrorCategory.InvalidOperation, Nothing))
        End Try
    End Sub

    Private Sub BatchNotify(insertCount As Integer)
        WriteProgress(New ProgressRecord(0, "SimplySql Bulk Copy", Me.DestinationTable) With {.CurrentOperation = String.Format("Inserted {0} rows.", insertCount)})
    End Sub
End Class
